
#include "stdafx.h"
#include "color.h"
#include ".\wf.h"
#include ".\GDIUtils.h"

#include ".\multipolygon.h"
#include ".\selectedpolygon.h"
#include ".\groupedpolygon.h"
#include "GLAddon\PsudoColorRGB.h"
extern		ColornameElem		ColornameTable[];
extern		int					NBCOLORNAME ;
extern		COLORREF			g_Colors[DHC_SIZE];
extern		unsigned int		g_Sizes[DHC_SIZE];
extern		CPoint				g_Origin;


CMultiPolygon::CMultiPolygon(void)
{
	m_pSelectedPolygons = NULL;

	m_pGroupedPolygons = NULL;
}

CMultiPolygon::~CMultiPolygon(void)
{
	Clear();
}

CPolygon* CMultiPolygon::GetPolygon(int Index) const 
{
	if( Index < 0 || Index >= NumPolygon() )
		return 0;

	return m_PolygonSet.at(Index);
}


bool CMultiPolygon::InsertPolygonAt(int Index,CPolygon* poly)
{
	if( Index < 0 || Index > NumPolygon() || poly==NULL )
		return false;
	
	m_PolygonSet.insert(m_PolygonSet.begin()+Index,poly);

	return true;
}


// add a polygon
// if bAppend add it to the tail, or else add it at the head
bool CMultiPolygon::AddPolygon(CPolygon* poly, bool bAppend )
{
	if(poly==NULL)
		return false;
	
	if( poly->NumVertex() == 0 )
		return false;

	if( bAppend )
		m_PolygonSet.push_back( poly );
	else
		m_PolygonSet.insert( m_PolygonSet.begin(), poly );

	return true;
}


bool CMultiPolygon::RemovePolygon(int Index)
{
	if( Index < 0 || Index>=NumPolygon() )
		return false;
	
	CPolygon *pPoly = GetPolygon( Index );
	delete pPoly;

	m_PolygonSet.erase( m_PolygonSet.begin() + Index );
	return true;
}

int CMultiPolygon::NumPolygon() const
{
	return (int)m_PolygonSet.size();
}

void CMultiPolygon::Clear()
{
	int Index= NumPolygon();// nothing to worry about
	while(Index>0 )
	{
		delete m_PolygonSet.at(Index-1);
		Index--;
	}	
	m_PolygonSet.clear();


	if( m_pGroupedPolygons )
		m_pGroupedPolygons->Clear();
}

// split the polygon at some vertex
void	CMultiPolygon::Split( int &nPoly, int &nVert )
{
	CPolygon *pPoly = GetPolygon( nPoly );
	bool	close = pPoly->GetClosed();
	CPolygon *pNewPoly = pPoly->Split( nVert );
	
	if( close )
	{
		nVert = 0;
	}
	else if( pNewPoly )
	{
		InsertPolygonAt( nPoly, pNewPoly );
		nVert = 0;
	}
}

// align other objects according to the top object
void	CMultiPolygon::AlignOnTopObject(BOOL bAlignTangent)
{
	int num = NumPolygon();
	if( num< 2 )
		return ;

	CPolygon *pTopPoly = this->GetPolygon(0);
	int nNumTopPoly = pTopPoly->NumVertex();

	// calculate the align path, this can be done by other methods
	std::vector<CVertex*>  AlignPath;
	for( int i=1; i<num; i++ )
	{
		AlignPath.push_back( pTopPoly->GetVertex((i-1)%nNumTopPoly) );
	}

	// align other objects by the path
	for( int i=1; i<num; i++ )
	{
		CPolygon *pPoly = GetPolygon(i);
		CVertex anchor = pPoly->GetHandle(POLYHNDL_ANCHOR);
		pPoly->Translate( AlignPath[i-1]->x - anchor.x, AlignPath[i-1]->y - anchor.y );
	}
}

// connect 2 polygons
void	CMultiPolygon::Connect( int nPoly1, int nPoly2 )
{
	assert( nPoly1 < nPoly2 );
	CPolygon *pPoly1 = GetPolygon( nPoly1 );
	CPolygon *pPoly2 = GetPolygon( nPoly2 );

	if( pPoly1->GetClosed() || pPoly2->GetClosed() )	// closed polygon can not be connected
		return ;

	// add the vertices of poly2 to poly1
	for( int i=0; i<pPoly2->NumVertex(); i++ )
	{
		CVertex *pV = pPoly2->GetVertex(i);
		pPoly1->AddVertex( new CVertex(pV->x, pV->y) );
	}

	// update the handles
	pPoly1->ComputeHandles();

	// remove poly2
	this->RemovePolygon( nPoly2 );
}

// show the vertex indices
void	CMultiPolygon::ShowVertexIndices( CDC *pDC )
{
	for( int i=0; i<NumPolygon(); i++  )
	{
		CPolygon *pPoly = GetPolygon( i );
		pPoly->ShowVertexIndices(pDC);
	}
}


// reset the colors of the polygons
void CMultiPolygon::ResetFilledColor(bool bAll)
{
	static int seed = 0;
	seed += NumPolygon();

	if( bAll )	// for all polygons
	{
		for( int i=0; i<NumPolygon(); i++  )
		{
			CPolygon *pPoly = GetPolygon( i );
			int idx = ( seed + i ) % NBCOLORNAME;
			pPoly->SetFilledColor(RGB(ColornameTable[idx].red, ColornameTable[idx].green, ColornameTable[idx].blue));
		}
	}
	else		// for selected polygons
	{
		int num = m_pSelectedPolygons->Size();
		for( int i=0; i<num; i++ )
		{
			CPolygon *pPoly = m_pSelectedPolygons->GetPolygon( i );
			int idx = ( seed + i ) % NBCOLORNAME;
			pPoly->SetFilledColor(RGB(ColornameTable[idx].red, ColornameTable[idx].green, ColornameTable[idx].blue));
		}
	}
}


// load animation script file of WF format
int		CMultiPolygon::LoadScriptFile( char *filename, bool bAdd )
{
	if( !bAdd )
		Clear();

	int ok = TRUE;

	FILE *fp;

	if( (fp=fopen(filename, "rt"))==NULL )
	{
		ok = FALSE;
	}
	else
	{
		char line[256];
		char name[128];

		while (fgets (line, 256, fp)  &&  sscanf (line, "%s", name) == 1)
		{
			if (!LoadWFFile (name, true) )
			{
				ok = FALSE;
				break;
			}
		}

		fclose(fp);
	}

	return TRUE;
}

// load animation script file of ply format
int		CMultiPolygon::LoadScriptFilePLY( char *filename, bool bAdd )
{
	if( !bAdd )
		Clear();

	int ok = TRUE;

	FILE *fp;

	if( (fp=fopen(filename, "rt"))==NULL )
	{
		ok = FALSE;
	}
	else
	{
		char line[256];
		char name[128];

		while (fgets (line, 256, fp)  &&  sscanf (line, "%s", name) == 1)
		{
			if (!LoadPLYFile (name, true) )
			{
				ok = FALSE;
				break;
			}
		}

		fclose(fp);
	}

	return TRUE;
}

// load WF file
int		CMultiPolygon::LoadWFFile( char *filename, bool bAdd )
{
	// clear the old one
	CWFObject wf;
	if( !wf.Load( filename ) )
		return FALSE;

	if( !bAdd )
		Clear();

	return GetFromWF( wf );
}

// load PLY file
int		CMultiPolygon::LoadPLYFile( char *filename, bool bAdd )
{
	if( !bAdd )
		Clear();
	
	// begin a new group
	CGroupIdx newGroup;


	FILE *fp;
	if( (fp=fopen(filename, "rt"))==NULL )
		return FALSE;

	CString temp;
	temp.Format("%s",filename);
	m_fname = GetNameFromPath(temp);
	std::cout<<"#Loading from : "<<m_fname<<"\n"; 
	// skip the comment lines
	char str[256];
	fgets( str, 256, fp );
	fgets( str, 256, fp );

	// read the number of polygon
	int polynum;
	fscanf( fp, "%d\n", &polynum );

	for( int i=0; i<polynum; i++ )
	{
		CPolygon *pNewPoly = new CPolygon;
		int vertnum, closed, filled, r, g, b, lined, linewidth;
		fscanf( fp, "%d %d\n", &vertnum, &closed );
		fscanf( fp, "%d (%d,%d,%d)\n", &filled, &r, &g, &b );
		pNewPoly->SetClosed( closed ? true : false );
		pNewPoly->SetFilled( filled ? true : false );
		pNewPoly->SetFilledColor( RGB(r,g,b) );
		fscanf( fp, "%d %d (%d,%d,%d)\n", &linewidth, &lined, &r, &g, &b );
		pNewPoly->SetLineWidth( linewidth );
		pNewPoly->SetLined( lined ? true : false );
		pNewPoly->SetLinedColor( RGB(r,g,b) );
		
		for( int j=0; j<vertnum; j++ )
		{
			double x, y;
			fscanf(fp, "%lf %lf\n", &x, &y );
			pNewPoly->AddVertex( x, y );
		}

		pNewPoly->ComputeHandles();
		pNewPoly->VertexNormal();
		this->AddPolygon( pNewPoly, true );

		newGroup.Add( NumPolygon()-1 );
	}
	fclose(fp);
	
	//m_pGroupedPolygons->Add( newGroup );
	
	return TRUE;
}

int		CMultiPolygon::GetFromWF( CWFObject &wf )
{
	int index = 0;
    int EdgeBiginIndex=0;
	int EdgeEndIndex=0;

	// begin a new group
	CGroupIdx newGroup;

	CPolygon* poly =new CPolygon();

	vertex_list VertexLs = wf.GetVertexList();
	std::vector<CVertex*>::iterator pBegin = VertexLs.begin();
	std::vector<CVertex*>::iterator pEnd = VertexLs.end();
	assert( pEnd == (pBegin + wf.NumVertices()) );
	std::vector<CVertex*>::iterator p = pBegin;


	CVertex *v1 = new CVertex((*p)->x, (*p)->y);
	p++;
	CVertex *v2 = new CVertex((*p)->x,(*p)->y);

	poly->AddVertex(v1);
	poly->AddVertex(v2);

	edge_list pEdge = wf.GetEdgeList();

	// suppose the vertex sequence is the sequence we input it from 0 to end
	while(index < wf.NumEdges() - 1 )
	{
		if( pEdge[index]->v1 != pEdge[index+1]->v0 )
		{
			if(pEdge[EdgeBiginIndex]->v0 == pEdge[EdgeEndIndex]->v1 )
				poly->SetClosed();	// set if is closed polygon
			EdgeBiginIndex=index+1;

			poly->ComputeHandles();
			// for WF polygons, we should flip vertically because the different coordinate system
			poly->Translate( g_Origin.x, 0 );
			poly->FlipVertical( g_Origin.y / 2 );
			poly->ReverseOrientation();
			AddPolygon(poly, true);	//a new polygon begins ,push the old first
			poly = new CPolygon();

			newGroup.Add( NumPolygon()-1 );

			p=pBegin+pEdge[index+1]->v0;
			v1 = new CVertex ((*p)->x ,(*p)->y );
			p=pBegin+pEdge[index+1]->v1;
			v2 = new CVertex( (*p)->x, (*p)->y );

			poly->AddVertex(v1);
			poly->AddVertex(v2);
		}
		else
		{
			if(pEdge[EdgeBiginIndex]->v0 != pEdge[index+1]->v1 )
			{
				p=pBegin+pEdge[index+1]->v1; //suppose the serial num 0 based
				v1 = new CVertex((*p)->x, (*p)->y);
				poly->AddVertex(v1);
			}
			EdgeEndIndex=index+1;
		}

		index++;
	}

	// add the last polygon
	if(pEdge[EdgeBiginIndex]->v0 == pEdge[EdgeEndIndex]->v1 )
		poly->SetClosed();	// set if is closed polygon

	poly->ComputeHandles();

	// for WF polygons, we should flip vertically because the different coordinate system
	poly->Translate( g_Origin.x, 0 );
	poly->FlipVertical( g_Origin.y / 2 );
	poly->ReverseOrientation();

	AddPolygon(poly, true);	// add the last polygon

	newGroup.Add( NumPolygon()-1 );
	m_pGroupedPolygons->Add( newGroup );

	return TRUE;
}


// save to PLY file
int		CMultiPolygon::SaveToPLYFile( char *filename )
{
	FILE *fp;
	if( (fp=fopen(filename, "wt"))==NULL )
		return FALSE;

	int polynum = NumPolygon();
    fprintf( fp, "# PLY -- polygons file\n" );
    fprintf( fp, "# Created by PolyMaker -- Internet Graphics Group, Microsoft Research Asia (c) 2003 \n\n");

	// number of polygons
	fprintf( fp, "%d\n", polynum );

	for( int i=0; i<polynum; i++)
	{
		fprintf(fp, "\n");
		// the i-th polygon
		CPolygon *pPoly = this->GetPolygon(i);

		// vertex number
		fprintf(fp, "%d		%d\n", pPoly->NumVertex(), pPoly->GetClosed()?1:0 );
		COLORREF color = pPoly->GetFilledColor();
		fprintf(fp, "%d		(%d,%d,%d)\n", pPoly->GetFilled()?1:0, GetRValue(color), GetGValue(color), GetBValue(color) );
		color = pPoly->GetLinedColor();
		fprintf(fp, "%d		%d	(%d,%d,%d)\n", pPoly->GetLineWidth(), pPoly->GetLined()?1:0, GetRValue(color), GetGValue(color), GetBValue(color) );

		// vertex coordinates
		for( int j=0; j<pPoly->NumVertex(); j++ )
		{
			CVertex *pV = pPoly->GetVertex(j);
			fprintf(fp, "%f %f\n", pV->x, pV->y );
		}
	}

	fclose( fp );
	return TRUE;
}


// save to WF file
int		CMultiPolygon::SaveToWFFile( char *filename )
{
	int polynum = NumPolygon();
	if( polynum == 0 )
		return FALSE;

	// flip vertically because of the different coordinate system
	for(int i=0;i<polynum;i++ )
	{
		CPolygon *pPoly = GetPolygon(i);
		pPoly->Translate( -g_Origin.x, 0 );
		pPoly->FlipVertical( g_Origin.y / 2 );
		pPoly->ReverseOrientation();
	}

	FILE *fp;
	if( (fp=fopen(filename, "wt"))==NULL )
		return FALSE;


	// write the header infos
    fprintf( fp, "#WFMESH2\n" );
    fprintf( fp, "# Created by PolyMaker -- Internet Graphics Group, Microsoft Research Asia (c) 2003 \n");
	fprintf( fp, "# %d polygons\n", polynum );
    //fprintf (fp, "# %d vertices %d edges -- ", 
    //        nVertices, nEdges );
    //if(m_Polygon.IfClosed())
    //    fprintf(fp, " closed\n" );
    //else
    //    fprintf(fp, " non-closed\n" );

	std::vector<int> num_verts;
	num_verts.push_back( 0 );
	int counter = 0;
	int i=1;
	for(int i=0; i<polynum; i++ )
	{
		counter += GetPolygon(i-1)->NumVertex() ;
		num_verts.push_back( counter );
	}
	counter += GetPolygon(i-1)->NumVertex() ;	// total number of vertices

    // save the vertices
    fprintf(fp, "# vertices %d\n", counter );
	int Index = 0;
	for( int i=0; i<polynum; i++ )
	{
		CPolygon *pPoly = GetPolygon(i);
		for( int j=0; j<pPoly->NumVertex(); j++ )
		{
			CVertex *pV = pPoly->GetVertex( j );
			fprintf(fp,"%f %f 0.0 %d\n", pV->x, pV->y, Index);
			Index++ ;
		}
	}
	assert( Index == counter );

    // save the edges
    fprintf(fp, "\n");
    fprintf(fp, "# edges\n" );
	for( int i=0; i<polynum; i++ )
	{
		CPolygon *pPoly = GetPolygon(i);
		int j=0;
		for( j=0; j<pPoly->NumVertex()-1; j++ )
		{
			fprintf(fp, "%d %d\n", j+num_verts[i], j+1+num_verts[i]);
		}

		if( pPoly->GetClosed() )
		{
			fprintf(fp, "%d %d\n", j+num_verts[i], num_verts[i]);
		}
	}

	fclose( fp );


	// restore to the original polygon
	// flip vertically because of the different coordinate system
	for(int i=0;i<polynum;i++ )
	{
		CPolygon *pPoly = GetPolygon(i);
		pPoly->ReverseOrientation();
		pPoly->FlipVertical( g_Origin.y / 2 );
		pPoly->Translate( g_Origin.x, 0 );
	}

	return TRUE;
}

void CMultiPolygon::Draw( CDC *pDC, BOOL bDrawVertex, BOOL bColoredEdge,bool bDrawNormal )
{
	//for( int i=0; i<NumPolygon(); i++  )
	for( int i=NumPolygon()-1; i>=0; i--  )
	{
		CPolygon *pPoly = GetPolygon( i );
		
		// draw the polygon
		pPoly->Draw( pDC, bColoredEdge );

		// if it is selected, draw the edges
		if( m_pSelectedPolygons->IsSelected(i)>-1 )
		{
			CPen pen(PS_SOLID, g_Sizes[DHC_SELECTED_POLYGON], g_Colors[DHC_SELECTED_POLYGON] );
			CGDISelectObject<CPen> tmp(pDC, &pen);
			pPoly->Draw( pDC, &pen );
		}

		// if draw vertex
		if( bDrawVertex )
			pPoly->DrawVertices( pDC );
		// if draw normal
		if(bDrawNormal)
			pPoly->DrawNormal(pDC);			
	}
}

void CMultiPolygon::DrawInGL( CSize window_size, float fGLViewWidth, float fGLViewHeight,bool bDrawCurve, bool bDrawVertex,bool bDrawNormal,CPseudoColorRGB &PsdC,Vector2 Origin/*=Vector2(0,0)*/ )
{
	for( int i=NumPolygon()-1; i>=0; i--)
	{
		CPolygon *pPoly = GetPolygon( i );
		pPoly->DrawInGL(window_size, fGLViewWidth, fGLViewHeight, bDrawCurve,bDrawVertex,bDrawNormal,PsdC,Origin);// draw the polygon
	}		
}

void CMultiPolygon::DrawHandles( CDC *pDC )
{
	for( int i=0; i<NumPolygon(); i++  )
	{
		GetPolygon( i )->DrawHandles( pDC );
	}
}

int		CMultiPolygon::FindFirstPolyContainPoint( double px, double py )
{
	for( int i=0; i<NumPolygon(); i++  )
	{
		CPolygon *pPoly = GetPolygon( i );
		if( pPoly->PolyContainPoint( px, py ) )
			return i;
	}

	return -1;
}


int		CMultiPolygon::FindFirstVertexOnEdge( int &nPolyIndex, int &nVertexIndex, double &ratio, double px, double py )
{
	nVertexIndex = -1;
	for( int i=0; i<NumPolygon(); i++  )
	{
		CPolygon *pPoly = GetPolygon( i );
		nVertexIndex = pPoly->PolyVertexOnEdge( ratio, px, py );
		if( nVertexIndex > -1 )
		{
			nPolyIndex = i;
			return TRUE;
		}
	}

	return FALSE;
}

int		CMultiPolygon::FindFirstVertexOnPoint( int &nPolyIndex, int &nVertexIndex, double px, double py )
{
	nVertexIndex = -1;
	for( int i=0; i<NumPolygon(); i++  )
	{
		CPolygon *pPoly = GetPolygon( i );
		nVertexIndex = pPoly->PolyVertexOnPoint( px, py );
		if( nVertexIndex > -1 )
		{
			nPolyIndex = i;
			return TRUE;
		}
	}

	return FALSE;
}


void	CMultiPolygon::Zoom( double ratio )
{
	for( int i=0; i<NumPolygon(); i++ )
	{
		this->GetPolygon(i)->Zoom( ratio );
	}

	// zoom the handles of the selections
	m_pSelectedPolygons->ZoomHandles( ratio );
}


void	CMultiPolygon::ChangeOrder( int idx, int offset )
{
	if( offset == 0 )
		return;

	int idxend = idx + offset ;
	assert( idx>=0 && idx<NumPolygon() );
	assert( idxend>=0 && idxend<NumPolygon() );

	// the swap operation is not totally correct...
	//CPolygon *pTemp = m_PolygonSet[idx ];
	//m_PolygonSet[idx] = m_PolygonSet[idxend];
	//m_PolygonSet[idxend] = pTemp;
	if( offset > 0 )
	{
		CPolygon *pTemp = m_PolygonSet[idx ];
		for( int i=idx; i<idxend; i++ )
		{
			m_PolygonSet[i] = m_PolygonSet[i+1];
		}
		m_PolygonSet[idxend] = pTemp;
	}
	else
	{
		CPolygon *pTemp = m_PolygonSet[idx ];
		for( int i=idx; i>idxend; i-- )
		{
			m_PolygonSet[i] = m_PolygonSet[i-1];
		}
		m_PolygonSet[idxend] = pTemp;
	}
}

